TL;DR

In this assignment we implemented Dataset and Dataloades classes on students faces data that we uploaded to the drive.
We chose augmentations that we think will be good for use in face recognition and we applied them on our dataset.
Moreover, we implemented OpenCV face detection algorithm and swapped between 2 faces in different images (there are examples in the code).

Students Details:

  • Guy Kabiri
  • Tomer Dwek

Imports

In [1]:
import torch
import numpy as np
import random
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision
from torch.utils.data import Dataset , DataLoader
from sklearn.model_selection import train_test_split
import albumentations as A
import cv2
import os

Load The Data

In [2]:
from google.colab import drive
drive.mount("/content/drive", force_remount=True)
Mounted at /content/drive
In [3]:
GOOGLE_DRIVE_PATH_AFTER_MY_DRIVE = 'Computer Vision/Afeka_CV_2021/'
GOOGLE_DRIVE_PATH = os.path.join('drive', 'My Drive', GOOGLE_DRIVE_PATH_AFTER_MY_DRIVE)
In [4]:
SEED = 0
class FacesData:
    def __init__(self, path):
        self.images = []
        self.labels = []

        for folder in os.listdir(path):
          folder_path = os.path.join(path, folder)
          for file in os.listdir(folder_path):
            self.labels.append(folder)
            img_path = os.path.join(folder_path, file)
            img = cv2.imread(img_path)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            self.images.append(img)
        
        self.images = np.array(self.images)
        self.labels = np.array(self.labels)


    def load_data(self, train_size=.8):

        if train_size > 1 or train_size < 0:
            raise ValueError("Split sizes can not be greater than 1 or less than 0")

        test_size = 1 - train_size
        x_train, x_test, y_train, y_test = train_test_split(self.images, self.labels, test_size=test_size, random_state=SEED)

        return (x_train, y_train), (x_test, y_test)

Display Images

In [5]:
students_number = len(os.listdir(GOOGLE_DRIVE_PATH))
plt.figure(figsize=(25, 10))
for index, folder in zip(range(students_number), os.listdir(GOOGLE_DRIVE_PATH)):
    plt.subplot(4, 10, index + 1)
    folder_path = os.path.join(GOOGLE_DRIVE_PATH, folder)
    img_path = os.path.join(folder_path, os.listdir(folder_path)[0])
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    plt.title(folder)
    plt.axis("off")
    plt.imshow(img)    
plt.show()

DataSet && DataLoader

In [6]:
class FacesDataSet(Dataset):
  def __init__(self, data, labels, transforms=None):
    self.x = data
    self.y = labels
    self.transforms = transforms

  def __getitem__(self, idx):
    x = self.x[idx]
    y = self.y[idx]
    x = cv2.resize(x, (224, 224), cv2.INTER_AREA)
    if self.transforms:
      x = self.transforms(image = x)
      x = x['image']
      x = torch.from_numpy(x)

    return x, y

  def __len__(self):
    return len(self.x)

Albumentations

Channel Shuffle

Randomly rearrange channels of the input RGB image.
image

Motion Blur

Docstring for MotionBlur Apply motion blur to the input image using a random-sized kernel.

image

If the blur_limit get high values, it may affect on the image recognition.
for example:
image

GaussNoise

Apply gaussian noise to the input image.

image

RandomBrightness

Randomly change the brightness of the input image.

image

If the limit get too high or too low values we will see too bright/dark image.
for example:
image

Rotate

Rotate the input by an angle selected randomly from the uniform distribution.
image

Blur

Decreases image quality by downscaling and upscaling back.

image

The Blur limit should get low values in order not to ruin the image.
for example:
image

Apply Transformations

In [7]:
transformations = A.Compose([
                                 A.ChannelShuffle(p=0.3),
                                 A.MotionBlur(blur_limit=10, p=0.3),
                                 A.RandomBrightness(limit=(-0.2, 0.2), p=0.3),
                                 A.Rotate(limit=(0,360), p=0.3),
                                 A.Blur(blur_limit=10, p=0.3),
                                 A.GaussNoise(var_limit=(0, 500), p=0.3),
])
In [8]:
faces = FacesData(GOOGLE_DRIVE_PATH)
(x_train, y_train), (x_test, y_test)= faces.load_data()
/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:16: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  app.launch_new_instance()
In [9]:
trainset = FacesDataSet(x_train, y_train, transformations)
testset = FacesDataSet(x_test, y_test, transformations)
In [10]:
BS=32
faces_loader_train = DataLoader(dataset=trainset, batch_size= BS, shuffle=True)
faces_loader_test = DataLoader(dataset=testset, batch_size= BS, shuffle=False)
In [11]:
imgs, lbls = next(iter(faces_loader_train))
In [12]:
plt.figure(figsize=(20, 10))
for index, img, lbl in zip(range(BS), imgs, lbls):
    plt.subplot(4, 8, index + 1)
    plt.title(lbl)
    plt.axis("off")
    plt.imshow(img)
    
plt.show()

Detect Face From Image

In [13]:
def detect_face(img):
    # img = np.array(img)
    cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')
    gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    detected = cascade.detectMultiScale(gray_img, 1.3, 5)
    return detected[0]
In [14]:
ids = [6, 7, 8, 12, 14]
plt.figure(figsize=(20, 10))
for index, id in zip(range(len(ids)), ids):
    plt.subplot(1, 5, index + 1)
    det = detect_face(x_train[id])
    image = x_train[id].copy()
    detected = cv2.rectangle(image, (det[0], det[1]), (det[0]+det[2], det[1]+det[3]), (0, 255, 0), 7)
    plt.title(y_train[id])
    plt.axis("off")
    plt.imshow(detected)
    
plt.show()

Swap Face

In [15]:
#Change the face of the first image to the face in the second image
def plant_face(img1, img2):
    det1 = detect_face(img1)
    det2 = detect_face(img2)
    img1_cpy = img1.copy()
    face2 = img2[det2[1]:det2[1]+det2[3],det2[0]:det2[0]+det2[2]]
    face2 = cv2.resize(face2,(det1[3],det1[2]),cv2.INTER_LINEAR)
    img1_cpy[det1[1]:det1[1]+det1[3],det1[0]:det1[0]+det1[2]] = face2 #swap the faces
    return img1_cpy
In [19]:
tuple_indexes = [(6, 7), (7, 8), (8, 12), (12, 14), (14, 6)]

for index, (id1, id2) in zip(range(len(tuple_indexes)), tuple_indexes):
  swapped = plant_face(x_train[id1],x_train[id2])

  plt.figure(figsize=(20, 10)) 
  plt.subplot(1, 3, 1)
  plt.title(y_train[id1])
  plt.axis("off")
  plt.imshow(x_train[id1])

  plt.subplot(1, 3, 2)
  plt.title(y_train[id2])
  plt.axis("off")
  plt.imshow(x_train[id2])

  plt.subplot(1, 3, 3)
  plt.title("Face Swapped Image")
  plt.axis("off")
  plt.imshow(swapped)
    
plt.show()